package lib

import (
	"ManageServer/defs/dbs"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	uuid "github.com/satori/go.uuid"
	"reflect"
)

func Check() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 后台登录不进行 session 验证
		if IsRouteIgnore(c, "session") {
			c.Next()
			return
		}

		dbsession, err := Get(c)
		if err != nil {
			c.JSON(200, gin.H{
				"code": "401",
				"msg":  "用户未登录",
				"data": map[string]interface{}{},
			})
			c.Abort()
			return
		}

		var value dbs.S_Session_V
		err = json.Unmarshal(dbsession.V, &value)
		if err != nil {
			c.JSON(200, gin.H{
				"code": "401",
				"msg":  "用户未登录",
				"data": map[string]interface{}{},
			})
			c.Abort()
			return
		}

		if value.UserId == 0 {
			c.JSON(200, gin.H{
				"code": "401",
				"msg":  "用户未登录",
				"data": map[string]interface{}{},
			})
			c.Abort()
			return
		}

		// 当前用户登录信息
		c.Set("user_id", value.UserId)
		c.Set("phone", value.Phone)
		c.Set("role", value.Role)
		c.Set("name", value.Name)
		c.Set("platform", value.Platform)
		c.Set("department", value.Department)
		c.Set("open_id", value.OpenID)
		c.Next()
	}
}

func Get(c *gin.Context) (dbsession dbs.DbSession, err error) {
	// 获取当前 token
	session := sessions.Default(c)
	token := session.Get("token")
	if token == nil {
		return dbsession, errors.New("无效的cookie")
	}
	tokenStr, isTrue := token.(string)
	if !isTrue {
		return dbsession, errors.New("token 类型不正确")
	}

	// 获取当前 session
	dbsession.K = tokenStr
	err = dbsession.Get()
	if err != nil {
		return dbsession, err
	}
	return dbsession, nil
}

func NewSession(c *gin.Context, value dbs.S_Session_V) (err error) {
	// 初始化 session
	var dbSession dbs.DbSession
	session := sessions.Default(c)

	dbSession.UserID = value.UserId
	dbSession.Platform = value.Platform

	// 生成 token
	dbSession.K = uuid.NewV4().String()

	dbSession.V, err = json.Marshal(value)
	if err != nil {
		return
	}
	err = dbSession.Create()
	if err != nil {
		return
	}

	session.Set("token", dbSession.K)
	options := sessions.Options{
		Path:   "/",
		MaxAge: 604800,
	}
	session.Options(options)
	err = session.Save()

	return
}

func UpdateSession(c *gin.Context, userId uint, platform string, value dbs.S_Session_V) (err error) {
	// Update 才是实际的登录/登出 方法, 目的是保持 session 中存在 open_id
	// 获取当前session
	dbsession, err := Get(c)
	if err != nil {
		return errors.New("无效的 token")
	}

	// 更新 session 为空时，保留原 session 中的 open_id
	// 获取当前 session open_id
	var currentValue dbs.S_Session_V
	err = json.Unmarshal(dbsession.V, &currentValue)
	if err != nil {
		return errors.New("session信息有误")
	}
	// 更新 session 的 open_id 为空时
	if value.OpenID == "" {
		value.OpenID = currentValue.OpenID
	}

	dbsession.V, err = json.Marshal(value)
	if err != nil {
		return
	}

	err = dbsession.Update(map[string]interface{}{
		"v":        dbsession.V,
		"user_id":  userId,
		"platform": platform,
	})
	if err != nil {
		return
	}
	//成功登录设置当前登录用户及平台
	c.Set("user_id", userId)
	c.Set("platform", platform)
	userid, bl := c.Get("user_id")
	if !bl {
		return errors.New(" get session userid is err")
	}
	fmt.Println("c.Get :  ", userid)
	return nil
}

func DeleteSession(userid uint, platform, token string) (err error) {
	var dbsession dbs.DbSession
	dbsession.UserID = userid
	dbsession.Platform = platform
	dbsession.K = token
	err = dbsession.Delete()
	if err != nil {
		return
	}
	return nil
}

func OfflineByOpenID(openID string, platform string, token string) (err error) {
	// 用户下线：解绑当前 openid 与 下线用户的关联
	if openID == "" {
		fmt.Println("openID 参数不存在,不进行下线操作")
		return
	}
	var user dbs.DbUser
	user.OpenID = openID
	err = user.Get()
	if err != nil && gorm.IsRecordNotFoundError(err) {
		err = nil
	}
	if err != nil {
		return
	}

	var dbsession dbs.DbSession
	dbsession.UserID = user.ID
	dbsession.Platform = platform
	dbsession.K = token
	err = dbsession.Delete()
	if err != nil {
		return
	}

	err = user.Update(map[string]interface{}{"open_id": ""})
	if err != nil {
		return
	}
	return
}

func OfflineByUserID(user_id uint, platform string, token string) (err error) {
	// 将 同用户同平台的登录记录下线，并解除微信与用户之间的关系
	if user_id == 0 {
		return errors.New("UserID 参数不存在")
	}

	// check null
	var user dbs.DbUser
	user.ID = user_id
	err = user.Get()
	if err != nil && gorm.IsRecordNotFoundError(err) {
		err = nil
	}
	if err != nil {
		return
	}

	var dbsession dbs.DbSession
	var dbsessions []dbs.DbSession
	// 获取 待下线的 session
	dbsessions, err = dbsession.Query(dbs.S_Session_List{
		UserID:   user_id,
		Platform: platform,
		K:        token,
	})
	if err != nil {
		return
	}
	// 遍历处理,正常情况下，dbsessions 中仅含有 0-1 个值
	for _, session := range dbsessions {
		// 获取对应 session 中的 open_id
		var value dbs.S_Session_V
		err = json.Unmarshal(session.V, &value)
		if err != nil {
			return errors.New("session信息有误")
		}

		session.V, err = json.Marshal(dbs.S_Session_V{
			OpenID: value.OpenID,
		})
		if err != nil {
			return
		}

		err = session.Update(map[string]interface{}{
			"v":        session.V,
			"user_id":  0,
			"platform": "",
		})
		if err != nil {
			return
		}
	}

	// web 平台下线 不解除绑定关系
	if platform == "h5" {
		err = user.Update(map[string]interface{}{"open_id": ""})
		if err != nil {
			return
		}
		return
	}

	return
}

func SetSessionKey(c *gin.Context, k string, v interface{}) (err error) {
	// 设置 session 中某一 Key 的值，更新到数据库
	dbsession, err := Get(c)
	if err != nil {
		return
	}

	var value dbs.S_Session_V
	err = json.Unmarshal(dbsession.V, &value)

	r_value := reflect.ValueOf(&value)
	field := r_value.Elem().FieldByName(k)
	field.Set(reflect.ValueOf(v)) // 设置值

	dbsession.V, err = json.Marshal(value)
	if err != nil {
		return
	}

	err = dbsession.Update(map[string]interface{}{
		"v": dbsession.V,
	})
	if err != nil {
		return
	}
	return
}
